package com.sprouting.ops.controller;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sprouting.ops.annotations.invoke.IdempotentSingle;
import com.sprouting.ops.interfaces.CacheKey;
import com.sprouting.ops.interfaces.ConstantBase;
import com.sprouting.ops.interfaces.ConstantPower;
import com.sprouting.ops.model.*;
import com.sprouting.ops.model.bo.UserSaveBo;
import com.sprouting.ops.model.bo.UserUpdBo;
import com.sprouting.ops.model.qo.UserQo;
import com.sprouting.ops.model.vo.UserCompositeVo;
import com.sprouting.ops.service.DromaraFileStorageService;
import com.sprouting.ops.service.OpsDepService;
import com.sprouting.ops.service.OpsRoleService;
import com.sprouting.ops.service.OpsUserService;
import com.sprouting.ops.utils.CheckTool;
import com.sprouting.ops.utils.EntityTool;
import com.sprouting.ops.utils.IdTool;
import com.sprouting.ops.utils.UserTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 用户控制层
 *
 * @author ：LX（长沙麓谷）
 * @date 创建时间： 2023/3/6 14:12
 */
@RestController
@RequestMapping("user")
public class OpsUserController {

    @Autowired
    private OpsUserService opsUserService;
    @Resource
    private OpsDepService opsDepService;
    @Resource
    private DromaraFileStorageService dromaraFileStorageService;
    @Resource
    private OpsRoleService opsRoleService;

    /**
     * 密码校验正则
     */
    private final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("^(?!.*\\s)(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[\\W_]).{8,16}$");



    /**
     * 变更用户的状态
     * @param userId 用户ID
     * @param status 状态 {@link com.sprouting.ops.interfaces.ConstantBase.UserStatus}
     * @return
     */
    @PostMapping("changeStatus")
    public ResultJson changeStatus(String userId, Integer status){
        if (CheckTool.checkNull(userId)){
            return ResultJson.errorCheck();
        }
        if (status == null){
            return ResultJson.error("无效的状态");
        }

        OpsUser user = opsUserService.getById(userId);
        if (user == null){
            return ResultJson.errorCheck();
        }
        // 安全性验证
        if (user.getId().equals(ConstantPower.Base.BASE_AUTH_ID)){
            return ResultJson.error("非法操作请求");
        }

        if (user.getStatus().equals(status)){
            return ResultJson.success();
        }
        user.setStatus(status);
        EntityTool.updBase(user);
        opsUserService.updateById(user);

        // 检查用户是否在登录中，登录中则更新缓存
        opsUserService.upOnLinedCache(userId);
        return ResultJson.success();
    }

    /**
     * 用户保存
     * @param bo 用户保存参数
     * @return
     */
    @PostMapping("saveUser")
    public ResultJson saveUser(@Validated UserSaveBo bo){
        OpsUser user = BeanUtil.copyProperties(bo, OpsUser.class);
        String msg = checkUser(user);
        if (msg != null){
            return ResultJson.error(msg);
        }
        msg = userAddCheck(user);
        if (msg != null){
            return ResultJson.error(msg);
        }

        if (bo.getSex() == null){
            bo.setSex(ConstantBase.Sex.UNKNOWN);
        }
        if (bo.getStatus() == null){
            bo.setStatus(ConstantBase.UserStatus.ACTIVATE);
        }
        user.setId(IdTool.getId());
        // 加盐加密
        user.setSalt(IdTool.getId());
        String md5Password = SaSecureUtil.md5BySalt(user.getPassword(), user.getSalt());
        user.setPassword(md5Password);
        // EntityTool.saveBase(user);

        // 头像处理
        DromaraFileStorage unifiedFileInfo = dromaraFileStorageService.toProductionBefore(user.getIco(), user.getId(), ConstantBase.FileSourceType.USER_ICO);

        // 角色
        List<OpsUserRole> userRoleList = buildUserRole(bo.getRoleIdList(), user);

        opsUserService.saveUser(user, unifiedFileInfo, userRoleList);
        return ResultJson.success();
    }

    /**
     * 删除用户
     * @param id 用户ID
     * @return
     */
    @PostMapping("delUser")
    public ResultJson delUser(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorCheck();
        }
        if (ConstantPower.Base.BASE_USER_ID.equals(id)){
            return ResultJson.error("基础数据，无法删除");
        }

        OpsUser user = opsUserService.getById(id);
        if (user == null){
            return ResultJson.success();
        }

        DromaraFileStorage unifiedFileInfo = dromaraFileStorageService.delFileBefore(user.getIco());
        opsUserService.delUser(user, unifiedFileInfo);
        return ResultJson.success();
    }

    /**
     * 更新用户
     * 密码不在这里更新
     * @param bo 用户更新参数
     * @return
     */
    @PostMapping("updUser")
    public ResultJson updUser(@Validated UserUpdBo bo){
        OpsUser user = BeanUtil.copyProperties(bo, OpsUser.class);
        String msg = checkUser(user);
        if (msg != null){
            return ResultJson.error(msg);
        }

        // 头像处理
        OpsUser db = opsUserService.getById(bo.getId());
        DromaraFileStorage[] unifiedFileInfo = dromaraFileStorageService.updFileBefore(bo.getIco(), db.getIco(), bo.getId(), ConstantBase.FileSourceType.USER_ICO);

        if (!db.getDepId().equals(bo.getDepId())){
            /// 更换了部门
            // 检查部门是否越权
            msg = checkDeptOverstep(bo.getDepId());
            if (msg != null){
                return ResultJson.error(msg);
            }

            // 检查用户是否部门的负责人
            List<OpsDep> leaderDepList = opsDepService.getByLeaderId(bo.getId());
            if (CollUtil.isNotEmpty(leaderDepList)){
                return ResultJson.error(String.format("必须先解除用户[%s]部门负责人身份才能迁移部门", leaderDepList.get(0).getName()));
            }
        }


        // 角色
        List<OpsUserRole> userRoleList = buildUserRole(bo.getRoleIdList(), user);

        opsUserService.updUser(user, unifiedFileInfo, userRoleList);
        return ResultJson.success();
    }

    /**
     * 获取部门下的用户
     * @param query 查询参数，部门必传
     * @return
     */
    @PostMapping("getUserPage")
    public ResultJson getUserPage(@Validated UserQo query){
        if (query == null){
            query = new UserQo();
        }
        OpsDep dept = opsDepService.getById(query.getDeptId());
        if (dept == null){
            // 防止通过部门id进行安全渗透，传入1模糊查询导致所有的部门数据泄露
            return ResultJson.errorCheck();
        }

        String msg = checkDeptOverstep(query.getDeptId());
        if (msg != null){
            return ResultJson.error("越权操作，您无权查询当前部门数据");
        }

        Page<UserCompositeVo> userPage = opsUserService.getUserPage(query.getPage(), query);
        List<UserCompositeVo> userSimpleList = userPage.getRecords();
        if (CollUtil.isEmpty(userSimpleList)){
            return ResultJson.success(userPage);
        }

        // 添加角色信息
        opsRoleService.addRole(userSimpleList);
        // 在线用户
        List<String> onLineUserIdList = getOnLineUserId();
        for (UserCompositeVo compositeVo : userSimpleList) {
            if (onLineUserIdList.contains(compositeVo.getId())){
                compositeVo.setOnLine(true);
            } else {
                compositeVo.setOnLine(false);
            }
        }

        return ResultJson.success(userPage);
    }



    /**
     * 登出，只登出当前设备的
     * @return
     */
    @PostMapping("logout")
    public ResultJson logout(){
        String tokenDevice = StpUtil.getLoginDevice();
        StpUtil.kickout(StpUtil.getLoginIdAsString(), tokenDevice);
        return ResultJson.success();
    }

    /**
     * 获取当前token的登录设备类型
     * @return
     */
    @GetMapping("getLoginDevice")
    public ResultJson getLoginDevice(){
        return ResultJson.success(StpUtil.getLoginDevice());
    }

    /**
     * 修改自身的密码
     * @param old 旧的密码
     * @param news 新的密码
     * @return
     */
    @IdempotentSingle
    @PostMapping("/updPassword")
    public ResultJson updPassword(String old, String news){
        if (CheckTool.checkNull(old) || CheckTool.checkNull(news)){
            return ResultJson.errorCheck();
        }

        old = old.trim();
        news = news.trim();

        old = opsUserService.decrypt(old);
        news = opsUserService.decrypt(news);

        // 修改自身的密码
        OpsUser user = UserTool.getLoginUser();
        String passwordOld = SaSecureUtil.md5BySalt(old, user.getSalt());
        if (!user.getPassword().equals(passwordOld)){
            return ResultJson.error("旧密码错误");
        }

        String passwordNews = SaSecureUtil.md5BySalt(news, user.getSalt());
        user.setPassword(passwordNews);
        user.setUpdateTime(new Date());
        opsUserService.updateById(user);
        return ResultJson.success(null);
    }

    /**
     * 获取当前登陆用户
     * @return
     */
    @PostMapping("getUser")
    public ResultJson<OpsUser> getUser(){
        //获取当前登陆用户，未登录或过期则抛出异常
        // String id = StpUtil.getLoginIdAsString();

        //获取当前用户的token的session
        SaSession session = StpUtil.getSession();
        OpsUser user = (OpsUser) session.get(CacheKey.SaTokenSession.SESSION_KEY_USER);
        return ResultJson.success(user);
    }

    /**
     * 获取在线用户ID
     * @return
     */
    private List<String> getOnLineUserId(){
        // 在线的用户ID
        List<String> onLine = new ArrayList<>();
        // 添加是否在线
        List<String> list = StpUtil.searchSessionId("", 0, -1, false);
        for (String loginId : list) {
            SaSession session = StpUtil.getSessionBySessionId(loginId);
            OpsUser model = session.getModel(CacheKey.SaTokenSession.SESSION_KEY_USER, OpsUser.class);
            onLine.add(model.getId());
        }

        return onLine;
    }

    /**
     * 检查更新部门是否越权
     * 检查当前的部门是否操作人员的下级部门
     * @param depId 部门ID
     * @return 非null有错误
     */
    private String checkDeptOverstep(String depId){
        OpsUser loginUser = UserTool.getLoginUser();
        if (StrUtil.isNotEmpty(depId)){
            if (!loginUser.getDepId().equals(depId)){
                // 检查是否越权,当前人可操作的部门范围
                List<OpsDep> allSon = opsDepService.getAllSon(loginUser.getDepId());
                if (CollUtil.isNotEmpty(allSon)){
                    List<String> sonDepIdList = allSon.stream().map(OpsDep::getId).collect(Collectors.toList());
                    if (!sonDepIdList.contains(depId)){
                        return "越权操作，您无权将人员转移到该部门";
                    }
                }
            }
        }
        return null;
    }

    /**
     * 获取用户登录信息
     */
    private void getLoginInfo(){
        // 获取登录会话账号ID，这里就是用户ID
        String loginIdAsString = StpUtil.getLoginIdAsString();

        // 获取当前会话账号id, 如果未登录，则返回null
        // StpUtil.getLoginIdDefaultNull();

        // 获取当前会话的token值
        String tokenValue = StpUtil.getTokenValue();

        // 获取当前`StpLogic`的token名称
        String tokenName = StpUtil.getTokenName();

        // 获取当前会话剩余有效期（单位：s，返回-1代表永久有效）
        long tokenTimeout = StpUtil.getTokenTimeout();

        // 获取当前会话的token信息参数
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        // 获取当前登录者的Session剩余有效时间
        long sessionTimeout = StpUtil.getSessionTimeout();
        // token的session有效期
        long tokenSessionTimeout = StpUtil.getTokenSessionTimeout();
    }

    /**
     * 构建用户和角色的关联关系
     * @param roleIdList 角色
     * @param user 用户信息
     * @return
     */
    private List<OpsUserRole> buildUserRole(List<String> roleIdList, OpsUser user){
        List<OpsUserRole> userRoleList = new ArrayList<>();
        if (CollUtil.isNotEmpty(roleIdList)){
            for (String roleId : roleIdList) {
                OpsUserRole userRole = new OpsUserRole();
                userRole.setId(IdTool.getId());
                userRole.setUserId(user.getId());
                userRole.setRoleId(roleId);
                userRoleList.add(userRole);
            }
        }

        return userRoleList;
    }

    /**
     * 新增时的一些特殊校验
     * 包含密码解密和默认数据处理
     * @param user 新增的用户
     * @return 非null则出现错误
     */
    private String userAddCheck(OpsUser user) {
        if (CheckTool.checkNull(user.getPassword())){
            return "请输入登录密码";

        } else {
            // 解密密码
            String decryptPassword = opsUserService.decrypt(user.getPassword());
            user.setPassword(decryptPassword);
        }

        // Matcher matcher = pattern.matcher(user.getPassword());
        // if (!matcher.matches()) {
        //     return "您输入的密码必须包含大小写字母、数字和特殊字符，长度为8—16!";
        // }

        return null;
    }

    /**
     * 检查用户信息
     * @param user 要检查的用户
     * @return 非空则有错误
     */
    private String checkUser(OpsUser user) {
        OpsDep dept = opsDepService.getById(user.getDepId());
        if (dept == null){
            return "部门信息错误，请检查是否有效部门";
        }

        OpsUser userDb = opsUserService.getByUserNameFilterId(user.getId(), user.getLoginName());
        if (userDb != null){
            return "登录名已注册，请勿重复注册";
        }

        if (StrUtil.isNotEmpty(user.getIdCard())){
            if (!Validator.isCitizenId(user.getIdCard())){
                return "身份证不合法";
            }
        }

        return null;
    }
}
